﻿using Cofe.Core;
using Cofe.Core.Implements;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Concurrent;

namespace Cofe.Core.Property
{
    public abstract class PropertyProviderBase : IPropertyProvider
    {
        #region Constructor

        protected PropertyProviderBase(params IPropertyDefinitions[] supportedDefinitions)
        {
            _supportedDefinitions = supportedDefinitions;
            _getPropertyDictionary = new ConcurrentDictionary<object, Func<PropertyPair>>();
            _setPropertyDictionary = new ConcurrentDictionary<object, Func<PropertyPair, bool>>();

            //For examples:
            //addGetProperty(CofeProperties.Name, () => Entity.Name, (pp) => Entity.Name = pp.Value);
        }

        #endregion

        #region Methods

        //private T callGetFunc<T>(Task<T> f)
        //{            
        //    f.RunSynchronously();
        //    return f.Result;
        //}
        //protected void registerProperties<T>(object property, Func<Task<T>> getFunc)
        //{
        //    registerProperties(property,
        //        () =>
        //        {
        //            return callGetFunc(getFunc());
        //        });
        //}

        protected void registerProperties(object property, Func<object> getFunc, Func<PropertyPair, bool> setFunc = null, bool valueCachable = true)
        {
            lock (_getPropertyDictionary)
            {
                Func<PropertyPair> f = () => PropertyPair.FromValueFunc(property, () => getFunc(), false, valueCachable);
                //if (_getPropertyDictionary.ContainsKey(property))
                    _getPropertyDictionary[property] = f;
                //else _getPropertyDictionary.Add(property, f);
            }
            if (setFunc != null)
                lock (_setPropertyDictionary)
                {
                    //if (_setPropertyDictionary.ContainsKey(property))
                        _setPropertyDictionary[property] = setFunc;
                    //else _setPropertyDictionary.Add(property, setFunc);
                }
        }

        protected void unregisterProperties(object property)
        {
            Func<PropertyPair> value;
            Func<PropertyPair, bool> value1;
            lock (_getPropertyDictionary)
                if (_getPropertyDictionary.ContainsKey(property))
                    _getPropertyDictionary.TryRemove(property, out value);
            lock (_setPropertyDictionary)
                if (_setPropertyDictionary.ContainsKey(property))
                    _setPropertyDictionary.TryRemove(property, out value1);
        }

        protected void registerAction(object property, Action<ParameterDic> actionFunc)
        {
            lock (_getPropertyDictionary)
                registerProperties(property, () => actionFunc);
        }

        protected void registerAction<T>(object property, Func<ParameterDic, T> actionFunc)
        {
            lock (_getPropertyDictionary)
                registerProperties(property, () => actionFunc);
        }

        protected void registerActionAsync(object property, Func<ParameterDic, Task> actionFunc)
        {
            lock (_getPropertyDictionary)
                registerProperties(property, () => actionFunc);
        }

        protected void registerActionAsync<T>(object property, Func<ParameterDic, Task<T>> actionFunc)
        {
            lock (_getPropertyDictionary)
                registerProperties(property, () => actionFunc);
        }

        public virtual bool IsPropertySupported(object property)
        {
            lock (_getPropertyDictionary)
                return _getPropertyDictionary.ContainsKey(property) || _additionalSupportedProperties.Contains(property);
        }

        public virtual bool IsPropertyReadOnly(object property)
        {
            lock (_setPropertyDictionary)
                return !_setPropertyDictionary.ContainsKey(property);
        }

        public virtual async Task<PropertyPair[]> GetPropertyAsync(object property)
        {
            //lock (_getAsyncPropertyDictionary)
            //if (_getAsyncPropertyDictionary.ContainsKey(property))
            //{
            //    var retVal = await _getAsyncPropertyDictionary[property]();
            //    return new PropertyPair[] { retVal };
            //}

            lock (_getPropertyDictionary)
                if (_getPropertyDictionary.ContainsKey(property))
                {
                    return new PropertyPair[] { _getPropertyDictionary[property]() };
                    //return new PropertyPair(property, _getPropertyDictionary[property]());
                    //var value = _getPropertyDictionary[property]();
                    //return new PropertyPair[] { new PropertyPair(property, value) };
                    //return Task<PropertyPair[]>.Run(() =>
                    //{
                    //    return new PropertyPair[] { new PropertyPair(property, value) };
                    //}
                    //);
                }

            return new PropertyPair[] { };
            //throw new NotSupportedException(property.ToString());
        }

        public virtual Task<bool> SetPropertyAsync(PropertyPair propertyPair)
        {
            lock (_setPropertyDictionary)
                if (_setPropertyDictionary.ContainsKey(propertyPair.Property))
                {
                    return Task<bool>.Run(() =>
                    {
                        return _setPropertyDictionary[propertyPair.Property](propertyPair);
                    }
                    );
                }

            throw new NotSupportedException();
        }

        protected virtual List<object> getSupportedProperties()
        {
            return _getPropertyDictionary.Keys.Union(_additionalSupportedProperties).Distinct().ToList();
        }

        public virtual Task RefreshAsync()
        {
            return new Task(() => { });
        }



        #endregion

        #region Data

        private IPropertyDictionary _propertyDictionary = null;
        protected List<object> _additionalSupportedProperties = new List<object>();
        private ConcurrentDictionary<object, Func<PropertyPair>> _getPropertyDictionary;
        //private Dictionary<object, Func<Task<PropertyPair>>> _getAsyncPropertyDictionary;
        private ConcurrentDictionary<object, Func<PropertyPair, bool>> _setPropertyDictionary;
        private IPropertyDefinitions[] _supportedDefinitions;
        private object[] _supportedProperties { get { return _getPropertyDictionary.Keys.ToArray(); } }

        #endregion

        #region Public Properties


        public IPropertyDictionary PropertyDictionary
        {
            get { return _propertyDictionary ?? CofeServices.ServiceLocater.FindService<IPropertyDictionary>(); }
            set { _propertyDictionary = value; }
        }

        public virtual IPropertyDefinitions[] SupportedDefinitions
        {
            get
            {
                return PropertyDictionary.CreatePropertyDefinitions(getSupportedProperties(), _supportedDefinitions);
            }
        }

        #endregion
    }


    public abstract class PropertyProviderBase<E> : PropertyProviderBase
    {
        #region Constructor

        protected PropertyProviderBase(E entry, params IPropertyDefinitions[] supportedDefinitions)
            : base(supportedDefinitions)
        {
            Entity = entry;
        }

        #endregion

        #region Methods


        #endregion

        #region Data

        #endregion

        #region Public Properties

        public E Entity { get; protected set; }

        #endregion
    }
}
